package br.com.vti.leilao.view.util;

import br.com.vti.leilao.model.entity.Model;
import br.com.vti.leilao.model.entity.Usuario;
import com.sun.faces.util.MessageFactory;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.faces.application.FacesMessage;
import javax.faces.component.EditableValueHolder;
import javax.faces.component.UIComponent;
import javax.faces.component.UIForm;
import javax.faces.component.UIInput;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.validation.ValidationException;
import org.apache.log4j.Logger;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;

@Component
public class JsfManager implements Serializable {

    private static final long serialVersionUID = 4304275062295930666L;
    public transient static final Logger logger = Logger.getLogger(JsfManager.class);

    /**
     * Retorna uma instância do FacesContext
     * @return FacesContext
     */
    public FacesContext getFacesContext() {
        return FacesContext.getCurrentInstance();
    }
    
    /**
     * Retorna uma instância do ExternalContext
     * @return ExternalContext
     */
    public ExternalContext getExternalContext() {
        return getFacesContext().getExternalContext();
    }

    /**
     * Retorna uma instancia do request.
     * @return a requisicao corrente.
     */
    public HttpServletRequest getHttpRequest() {
        return (HttpServletRequest) getExternalContext().getRequest();
    }

    /**
     * Retorna uma instancia do response.
     * @return a resposta corrente.
     */
    public HttpServletResponse getHttpResponse() {
        return (HttpServletResponse) getExternalContext().getResponse();
    }

    /**
     * Retorna uma instancia da sessao
     * @param criarNova 
     * @return a sessao corrente
     */
    public HttpSession getHttpSession(boolean criarNova) {
        HttpServletRequest request = (HttpServletRequest) getExternalContext().getRequest();
        return request.getSession(criarNova);
    }

    /**
     * Retorna um managed bean atraves do nome mapeado
     * @param name o nome do managed bean
     * @return o managed bean
     */
    public Object getBean(String name) {
        return getFacesContext().getApplication().getELResolver().getValue(getFacesContext().getELContext(), null, name);
    }

    /**
     * Mapeia um bean
     * @param name o nome do managed bean
     * @param valor o bean que deve ser mapeado
     */
    public void setBean(String name, Object valor) {
        if (name != null && !name.isEmpty() && valor != null) {
            getFacesContext().getApplication().getELResolver().setValue(getFacesContext().getELContext(), null, name, valor);
        }
    }

    /**
     * Retorna o caminho absoluto de um recurso
     * @param source
     * @return string
     */
    public String getRealPath(String source) {
        ServletContext context = (ServletContext) getExternalContext().getContext();
        return context.getRealPath(source);
    }
    
    public String getUrlParam(String nome) {
        String retorno = null;
        if (nome != null && !nome.isEmpty()) {
            Map<String, String> mapa = getExternalContext().getRequestParameterMap();
            if (mapa.containsKey(nome)) {
                retorno = mapa.get(nome);
            }
        }
        return retorno;
    }
    
    /**
     * Método utilizado para validação dos ComboBox's no form
     *
     * @param context
     * @param component
     * @param value
     */
    public void validarComboBox(FacesContext context, UIComponent component, Object value) {
        if (value instanceof Model) {
            Model<? extends Number> Model = (Model<? extends Number>) value;
            if (Model.getId() == null) {
                Object label = MessageFactory.getLabel(context, component);
                String required = "javax.faces.component.UIInput.REQUIRED";
                FacesMessage message = MessageFactory.getMessage(context, required, label);
                String msg = message.getSummary();
                UIInput ui = ((UIInput) component);
                ui.setValidatorMessage(msg);
                throw new ValidationException(msg);
            }
        }
    }

    /**
     * Limpa os campos do formulario.
     *
     * @param event o evento utilizado para disparar o metodo.
     */
    public void reset(ActionEvent event) {
        if (event != null) {
            UIForm form = findForm(event.getComponent(), true);
            if (form != null) {
                clearSubmittedValues(form);
            }
        }
    }

    /**
     * Retorna os clientId que contêm determinado nome.
     *
     * @param nomes nomes do campos.
     * @return os clientIds.
     */
    public String getRealName(String nomes) {
        String msg = "";
        List<String> lista = new ArrayList<String>();
        FacesContext facesContext = FacesContext.getCurrentInstance();
        UIViewRoot viewRoot = facesContext.getViewRoot();
        for (String nome : nomes.split(",")) {
            lista.addAll(findName(viewRoot, nome));
        }
        for (String aux : lista) {
            if (!msg.isEmpty()) {
                msg += ",";
            }
            msg += aux;
        }
        return msg;
    }

    private List<String> findName(UIComponent component, String name) {
        List<String> lista = new ArrayList<String>();
        Iterator<UIComponent> fac = component.getFacetsAndChildren();
        while (fac.hasNext()) {
            UIComponent aux = fac.next();
            if (aux != null) {
                if (aux.getId() != null && aux.getId().contains(name)) {
                    lista.add(aux.getClientId() != null ? aux.getClientId() : aux.getId());
                } else {
                    lista.addAll(findName(aux, name));
                }
            }
        }
        return lista;
    }

    /**
     * Retorna o formulario de um campo.
     *
     * @param component o campo do formulario.
     * @param acima especifica se o form deve ser buscado acima ou abaixo do
     * componente.
     * @return o formulario corrente.
     */
    public UIForm findForm(UIComponent component, boolean acima) {
        UIForm uiForm = null;
        if (component != null) {
            if (acima) {
                UIComponent aux = component.getParent();
                if (aux instanceof UIForm) {
                    uiForm = (UIForm) aux;
                } else {
                    uiForm = findForm(aux, acima);
                }
            } else {
                Iterator<UIComponent> fac = component.getFacetsAndChildren();
                while (uiForm == null && fac.hasNext()) {
                    UIComponent aux = fac.next();
                    if (aux instanceof UIForm) {
                        uiForm = (UIForm) aux;
                    } else {
                        uiForm = findForm(aux, acima);
                    }
                }
            }
        }
        return uiForm;
    }

    /**
     * Limpa o valor de um campo.
     *
     * @param component o component do formulario.
     */
    public void clearSubmittedValues(UIComponent component) {
        if (component != null) {
            Iterator<UIComponent> chld = component.getFacetsAndChildren();
            while (chld.hasNext()) {
                clearSubmittedValues(chld.next());
            }
            if (component instanceof EditableValueHolder) {
                EditableValueHolder evh = (EditableValueHolder) component;
                evh.setSubmittedValue(null);
                evh.setValue(null);
                evh.setLocalValueSet(false);
                evh.setValid(true);
            }
        }
    }

    /**
     * Utilizado para buscar um componente.
     *
     * @param component
     * @param id
     * @return UIComponent
     */
    private UIComponent findComponent(UIComponent component, String id) {
        UIComponent uiComponent = null;
        if (id.equals(component.getId())) {
            uiComponent = component;
        }
        Iterator<UIComponent> chld = component.getFacetsAndChildren();
        while (chld.hasNext()) {
            UIComponent found = findComponent(chld.next(), id);
            if (found != null) {
                uiComponent = found;
            }
        }
        return uiComponent;
    }

    /**
     * Metodo recebe id do componente de tela que deverá ser localizado a partir
     * do ViewRoot da pagina JSF.
     *
     * @param root UIViewRoot
     * @param id ClientId
     * @return UIComponent
     */
    public UIComponent lookup(UIViewRoot root, String id) {
        UIComponent uiComponent = null;
        if (id != null && !id.isEmpty()) {
            uiComponent = findComponent(root, id);
        }
        return uiComponent;
    }

    /**
     * Método utilizado para verificar se um componente é válido.
     *
     * @param clientId
     * @return boolean valido
     */
    public boolean valid(String clientId) {
        Boolean valid = false;
        FacesContext facesContext = FacesContext.getCurrentInstance();
        UIComponent component = lookup(facesContext.getViewRoot(), clientId);
        if (component != null && component.isRendered()) {
            if (component instanceof EditableValueHolder) {
                EditableValueHolder evh = (EditableValueHolder) component;
                valid = evh.isValid() || !facesContext.getMessages(component.getClientId()).hasNext();
            }
        }
        return valid;
    }

    /**
     * Retorna a mensagem de erro associada a um componente.
     *
     * @param clientId
     * @return mensagem
     */
    public String getErrorMsg(String clientId) {
        String msg = "";
        FacesContext facesContext = FacesContext.getCurrentInstance();
        UIComponent component = lookup(facesContext.getViewRoot(), clientId);
        List<FacesMessage> lista = facesContext.getMessageList(component.getClientId());
        if (!lista.isEmpty()) {
            FacesMessage facesMessage = lista.get(lista.size() - 1);
            msg = facesMessage.getDetail();
        }
        return msg;
    }
    
    public Usuario getUsuarioLogado() {
        Usuario usuario = null;
        Authentication authentication = (Authentication) getHttpRequest().getUserPrincipal();
        if (authentication != null && authentication.getPrincipal() instanceof Usuario) {
            usuario = (Usuario) authentication.getPrincipal();
        }
        return usuario;
    }
}
